How to download a file and save it to the documents directory with AFNetworking?
Asked Answered
T

8

50

I am using the AFNetworking library. I can't figure out how to download a file and save it to the documents directory.

Terisateriyaki answered 4/12, 2011 at 1:56 Comment(0)
P
138
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"..."]];
AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"filename"];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];

[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"Successfully downloaded file to %@", path);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

[operation start];
Paunchy answered 4/12, 2011 at 6:4 Comment(9)
You can even add a progress block: //Setup Upload block to return progress of file upload [operation setDownloadProgressBlock:^(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToRead) { float progress = totalBytesWritten / (float)totalBytesExpectedToRead; NSLog(@"Download Percentage: %f %%", progress*100); }];Ponderous
Be careful that the above code will download any response from the server to the output stream, so if the server responds with status code 404, the 404 page will be saved to the path specified. You must check on the success block for operation.response.statusCode.Ilyssa
Instead of manually checking the status code, just use operation.hasAcceptableStatusCodeLocation
NSURLRequest How to post or get one url?Gilpin
how to download only in success block ? @Ilyssa can you advise please?Sic
@Ilyssa No need, AFNetworking takes care of this for you.Ekaterinoslav
when i download video and click on back witho download completion. and again i click on video that time i get this error =>MediaPlayerErrorDomain Code=-11800 ... so plz help meWiburg
how to do this with POST with parameters?Woodward
how to call Get Type method with some header data and download a fileWood
S
31

I'm gonna bounce off @mattt's answer and post a version for AFNetworking 2.0 using AFHTTPRequestOperationManager.

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"filename"];

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
AFHTTPRequestOperation *op = [manager GET:@"http://example.com/file/to/download" 
                               parameters:nil
    success:^(AFHTTPRequestOperation *operation, id responseObject) {
         NSLog(@"successful download to %@", path);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
         NSLog(@"Error: %@", error);
    }];
op.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
Strapless answered 5/12, 2013 at 23:8 Comment(6)
I would definitely recommend doing it this way with the newer AFNetworking libraryWellgroomed
@Strapless [AFHTTPRequestOperationManager manager] will create new manager object using default AFJSONResponseSerializer, do you want that? I think it 's better to create AFNoneResponseSerialize to let the file untouchedTheophrastus
how can I get the download progress parameter in this ?Gemmagemmate
shshnk, look at my answer belowLaze
Is there a way to have the file cleaned up automatically if the download does not complete?Meniscus
when i download video and click on back witho download completion. and again i click on video that time i get this error =>MediaPlayerErrorDomain Code=-11800 ... so plz help meWiburg
T
5

I'm talking about AFNetworking 2.0

[AFHTTPRequestOperationManager manager] creates manager object with default AFJSONResponseSerializer, and it performs content types restriction. Take a look at this

- (BOOL)validateResponse:(NSHTTPURLResponse *)response
                    data:(NSData *)data
                   error:(NSError * __autoreleasing *)error

So we need to create a none response serializer and use AFHTTPRequestOperationManager as normal.

Here is the AFNoneResponseSerializer

@interface AFNoneResponseSerializer : AFHTTPResponseSerializer

+ (instancetype)serializer;

@end

@implementation AFNoneResponseSerializer

#pragma mark - Initialization
+ (instancetype)serializer
{
    return [[self alloc] init];
}

- (instancetype)init
{
    self = [super init];

    return self;
}

#pragma mark - AFURLResponseSerializer
- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error

{
    return data;
}

@end

Usage

self.manager = [AFHTTPRequestOperationManager manager];
self.manager.responseSerializer = [AFNoneResponseSerializer serializer];

[self.manager GET:@"https://sites.google.com/site/iphonesdktutorials/xml/Books.xml"
           parameters:parameters
              success:^(AFHTTPRequestOperation *operation, id responseObject)
    {
        if (success) {
            success(responseObject);
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if (failure) {
            failure(error);
        }
    }];

so that we can get the whole file without any serialization

Theophrastus answered 11/6, 2014 at 10:4 Comment(0)
B
4

Documentation page has example with section 'Creating a Download Task':

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];

NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
    NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
    return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
    NSLog(@"File downloaded to: %@", filePath);
}];
[downloadTask resume];

NB! Code work with iOS 7+ (tested with AFNetworking 2.5.1)

Bruner answered 13/2, 2015 at 10:45 Comment(1)
when i download video and click on back witho download completion. and again i click on video that time i get this error =>MediaPlayerErrorDomain Code=-11800 ... so plz help meWiburg
L
4

From AFNetworking docs. Save to loaded file to your documents. AFNetworking 3.0

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];

NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
    NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
    return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
    NSLog(@"File downloaded to: %@", filePath);
}];
[downloadTask resume];
Liquidambar answered 30/6, 2016 at 7:20 Comment(0)
A
2
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

manager.responseSerializer = [AFCompoundResponseSerializer serializer];

manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"application/octet-stream"];

AFHTTPRequestOperation *operation = [manager GET:url   parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    if (responseObject) {
        // your code here
    } else {
        // your code here
    }
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

}];

[operation start];

// manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject: @"application/octet-stream"]; can vary depending on what you expect

Acrimonious answered 21/1, 2015 at 5:6 Comment(0)
L
1

Yes, it is better to use AFNetworking 2.0 way with AFHTTPRequestOperationManager. With old way my file did download but for some reason didn't update in file system.

Appending to swilliam's answer, to show download progress, in AFNetworking 2.0 you do similarly - just set download progress block after setting output stream.

__weak SettingsTableViewController *weakSelf = self;

operation.outputStream = [NSOutputStream outputStreamToFileAtPath:newFilePath append:NO];

[operation setDownloadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToRead) {

    float progress = totalBytesWritten / (float)totalBytesExpectedToRead;

    NSString *progressMessage = [NSString stringWithFormat:@"%@ \n %.2f %% \n %@ / %@", @"Downloading ...", progress * 100, [weakSelf fileSizeStringWithSize:totalBytesWritten], [weakSelf fileSizeStringWithSize:totalBytesExpectedToRead]];

    [SVProgressHUD showProgress:progress status:progressMessage];
}];

This is my method to create bytes string:

- (NSString *)fileSizeStringWithSize:(long long)size
{
    NSString *sizeString;
    CGFloat f;

    if (size < 1024) {
        sizeString = [NSString stringWithFormat:@"%d %@", (int)size, @"bytes"];
    }
    else if ((size >= 1024)&&(size < (1024*1024))) {
        f = size / 1024.0f;
        sizeString = [NSString stringWithFormat:@"%.0f %@", f, @"Kb"];
    }
    else if (size >= (1024*1024)) {
        f = size / (1024.0f*1024.0f);
        sizeString = [NSString stringWithFormat:@"%.0f %@", f, @"Mb"];
    }

    return sizeString;
}
Laze answered 21/10, 2014 at 3:35 Comment(1)
when i download video and click on back witho download completion. and again i click on video that time i get this error =>MediaPlayerErrorDomain Code=-11800 ... so plz help meWiburg
F
0

In addition to the previous answers, with AFNetworking 2.5.0 and iOS7/8 I have found that that the extra step of opening the output stream is also needed to prevent the app from hanging (and eventually crashing from lack of memory).

operation.outputStream = [NSOutputStream outputStreamToFileAtPath:dest
                                                          append:NO];
[operation.outputStream open];
[operation start];
Flintlock answered 18/11, 2014 at 23:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.